﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MazeGeneration
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// All the nodes in the entire maze.
        /// </summary>
        private MazeNode[,] mNodes;

        /// <summary>
        /// The number of tiles in the entire maze.
        /// </summary>
        private Int32 MAP_WIDTH = 89;
        private Int32 MAP_HEIGHT = 45;

        /// <summary>
        /// The width and height of the maze in pixels.
        /// </summary>
        private Int32 mNodeSize = 10;

        /// <summary>
        /// Store a random number generator for reuse.
        /// </summary>
        private Random mRand;

        /// <summary>
        /// Constructor.
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Called when the mouse button is released.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            this.Invalidate();
        }

        /// <summary>
        /// Called when the form needs to be redrawn.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            for (Int32 y = 0; y < MAP_HEIGHT; y++)
            {
                for (Int32 x = 0; x < MAP_WIDTH; x++)
                {
                    mNodes[x, y].Render(e.Graphics);
                }
            }
        }

        /// <summary>
        /// Called when the form is initially loaded.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            // Used to pick random directions and such.
            mRand = new Random();

            // Create the maze.
            mNodes = new MazeNode[MAP_WIDTH, MAP_HEIGHT];

            // Loop through every node in the maze, and set it up.
            for (Int32 y = 0; y < MAP_HEIGHT; y++)
            {
                for (Int32 x = 0; x < MAP_WIDTH; x++)
                {
                    // We can only pass in the left and up, because the right and down have not been
                    // created at this point.
                    MazeNode left = null;
                    MazeNode up = null;

                    // Don't try finding the left and right if they don't actually exist.
                    if (x > 0)
                    {
                        left = mNodes[x - 1, y];
                    }
                    if (y > 0)
                    {
                        up = mNodes[x, y - 1];
                    }

                    // Create the node. The +15 is to just add a little spacing around the border.
                    mNodes[x, y] = new MazeNode(new Point(x * mNodeSize + 15, y * mNodeSize + 15), left, up, mNodeSize);
                }
            }

            // Now recursivly create the maze paths.
            CreatePath(mNodes[0, 0]);
        }

        /// <summary>
        /// Recursive fuction which creates the maze. It is a depth first search where
        /// each node we visit opens a path. Once a node has been visited, it cannot be
        /// visited again. Once we reach a point where there are no adject nodes that are
        /// unvisted, we start returned back up the tree continuing to search for nieghbours
        /// who have not been visited. 
        /// </summary>
        /// <param name="node"></param>
        private void CreatePath(MazeNode node)
        {
            node.pVisited = true;

            Int32 count = node.pUnvisitedNieghbours;

            while (0 != count)
            {

                Int32 dir = mRand.Next(count);

                MazeNode.WallIndicies type = MazeNode.WallIndicies.LEFT_INDEX;
                MazeNode next = node.GetNthUnvisitedNieghbour(dir, ref type);

                if (null != next)
                {
                    node.RemoveWall(type);
                    next.RemoveWall((MazeNode.WallIndicies)(((Int32)type + 2) % 4));
                    CreatePath(next);
                }

                count = node.pUnvisitedNieghbours;
            }
        }
    }
}
